package com.college.common.utils.security;

import com.alibaba.fastjson2.JSON;
import org.apache.commons.codec.binary.Base64;
import org.springframework.stereotype.Component;
import sun.misc.BASE64Decoder;
import sun.misc.BASE64Encoder;

import javax.crypto.Cipher;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.security.*;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.HashMap;

/**
 * @Description RSA非对称加密算法工具类
 * @author 小C工作室
 * @Date 2022/3/21 20:26
 */
@Component
public class RsaSecretUtils {

    private static final String RSA = "RSA";

    /**
     * 生成秘钥对
     *
     * @return
     * @throws Exception
     */
    public static KeyPair getKeyPair() throws Exception {
        KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
        keyPairGenerator.initialize(2048);
        return keyPairGenerator.generateKeyPair();
    }

    /**
     * 获取公钥(Base64编码)
     *
     * @param keyPair 秘钥对
     * @return
     */
    public static String getPublicKey(KeyPair keyPair) {
        PublicKey publicKey = keyPair.getPublic();
        byte[] bytes = publicKey.getEncoded();
        return Base64Utils.encode(bytes);
    }

    /**
     * 获取私钥(Base64编码)
     *
     * @param keyPair 秘钥对
     * @return
     */
    public static String getPrivateKey(KeyPair keyPair) {
        PrivateKey privateKey = keyPair.getPrivate();
        byte[] bytes = privateKey.getEncoded();
        return Base64Utils.encode(bytes);
    }

    /**
     * 将Base64编码后的公钥转换成PublicKey对象
     *
     * @param pubStr base64公钥
     * @return
     * @throws Exception
     */
    public static PublicKey string2PublicKey(String pubStr) throws Exception {
        try {
            byte[] buffer = Base64Utils.decode(pubStr);
            KeyFactory keyFactory = KeyFactory.getInstance(RSA);
            X509EncodedKeySpec keySpec = new X509EncodedKeySpec(buffer);
            return keyFactory.generatePublic(keySpec);
        } catch (NoSuchAlgorithmException e) {
            throw new Exception("无此算法");
        } catch (InvalidKeySpecException e) {
            throw new Exception("公钥非法");
        } catch (NullPointerException e) {
            throw new Exception("公钥数据为空");
        }
    }

    /**
     * 将Base64编码后的私钥转换成PrivateKey对象
     *
     * @param priStr base64私钥
     * @return
     * @throws Exception
     */
    public static PrivateKey string2PrivateKey(String priStr) throws Exception {
        byte[] keyBytes = Base64Utils.decode((priStr));
        PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(keyBytes);
        KeyFactory keyFactory = KeyFactory.getInstance("RSA");
        return keyFactory.generatePrivate(keySpec);
    }

    /**
     * 公钥加密
     *
     * @param content         明文
     * @param publicKeyBase64 公钥base64
     * @return
     */
    public static String publicEncrypt(String content, String publicKeyBase64) {
        return new String(Base64.encodeBase64(publicEncrypt(content.getBytes(), publicKeyBase64)));
    }

    /**
     * 公钥加密
     *
     * @param content         明文
     * @param publicKeyBase64 公钥base64
     * @return
     */
    public static byte[] publicEncrypt(byte[] content, String publicKeyBase64) {
        try {
            Cipher cipher = Cipher.getInstance(RSA);
            PublicKey publicKey = string2PublicKey(publicKeyBase64);
            cipher.init(Cipher.ENCRYPT_MODE, publicKey);
            int keyBit = getKeySize(publicKey);
            int inputLen = content.length;
            ByteArrayOutputStream out = new ByteArrayOutputStream();
            int offSet = 0;
            int step = keyBit / 8 - 11;

            for (int i = 0; inputLen - offSet > 0; offSet = i * step) {
                byte[] cache;
                if (inputLen - offSet > step) {
                    cache = cipher.doFinal(content, offSet, step);
                } else {
                    cache = cipher.doFinal(content, offSet, inputLen - offSet);
                }
                out.write(cache, 0, cache.length);
                ++i;
            }
            byte[] encryptedData = out.toByteArray();
            out.close();
            return encryptedData;
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    /**
     * 私钥解密
     *
     * @param content          密文
     * @param privateKeyBase64 私钥base64
     * @return
     * @throws Exception
     */
    public static byte[] privateDecrypt(byte[] content, String privateKeyBase64) throws Exception {
        try {
            Cipher cipher = Cipher.getInstance(RSA);
            PrivateKey privateKey = string2PrivateKey(privateKeyBase64);
            cipher.init(Cipher.DECRYPT_MODE, privateKey);
            int keyBit = getKeySize(privateKey);
            int inputLen = content.length;
            ByteArrayOutputStream out = new ByteArrayOutputStream();
            int offSet = 0;
            int step = keyBit / 8;

            for (int i = 0; inputLen - offSet > 0; offSet = i * step) {
                byte[] cache;
                if (inputLen - offSet > step) {
                    cache = cipher.doFinal(content, offSet, step);
                } else {
                    cache = cipher.doFinal(content, offSet, inputLen - offSet);
                }
                out.write(cache, 0, cache.length);
                ++i;
            }
            byte[] decryptedData = out.toByteArray();
            out.close();
            return decryptedData;
        } catch (Exception e) {
            return null;
        }
    }


    /**
     * 获取公钥长度
     *
     * @param publicKey 公钥
     * @return
     */
    public static int getKeySize(PublicKey publicKey) {
        RSAPublicKey rsaPublicKey = (RSAPublicKey) publicKey;
        return rsaPublicKey.getModulus().bitLength();
    }

    /**
     * 获取私钥长度
     *
     * @param privateKey 私钥
     * @return
     */
    public static int getKeySize(PrivateKey privateKey) {
        RSAPrivateKey rsaPrivateKey = (RSAPrivateKey) privateKey;
        return rsaPrivateKey.getModulus().bitLength();
    }

    /**
     * 字节数组转Base64编码
     *
     * @param bytes
     * @return
     */
    public String byte2Base64(byte[] bytes) {
        BASE64Encoder encoder = new BASE64Encoder();
        return encoder.encode(bytes);
    }

    /**
     * Base64编码转字节数组
     *
     * @param base64Key
     * @return
     * @throws IOException
     */
    public static byte[] base642Byte(String base64Key) throws IOException {
        BASE64Decoder decoder = new BASE64Decoder();
        return decoder.decodeBuffer(base64Key);
    }

    /**
     * 私钥解密
     *
     * @param base64 加密字符串
     */
    public static String privateDecrypt(String base64, String privateKeyBase64) {
        try {
            byte[] decryptBytes = privateDecrypt(base642Byte(base64), privateKeyBase64);
            return new String(decryptBytes);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return "";
    }

    public static void main(String[] args) throws Exception {
        // 1、生成私钥公钥
        KeyPair keyPair = getKeyPair();
        String publicKey = getPublicKey(keyPair);
        String privateKey = getPrivateKey(keyPair);
        System.out.println("公钥 = " + publicKey);
        System.out.println("私钥 = " + privateKey);
        // 2、公钥加密
        HashMap<String, String> map = new HashMap<>();
        map.put("idCardNo", "350625255001296047");
        map.put("mobile", "15059674216");
        map.put("distributorId", "42");
        String jsonString = JSON.toJSONString(map);
        System.out.println("明文 = " + jsonString);
        String x = publicEncrypt(jsonString, publicKey);
        System.out.println("公钥加密 = " + x);
        // 3、私钥解密
        System.out.println("私钥解密 = " + privateDecrypt(x, privateKey));
    }

}